package com.quanyan.ic.protocol.server;

import com.alibaba.fastjson.JSON;
import com.github.ltsopensource.jobclient.JobClient;
import com.quanyan.ic.cache.OrderMsgDao;
import com.quanyan.ic.cache.PlaceLightSettingDao;
import com.quanyan.ic.common.model.OrderMessage;
import com.quanyan.ic.constants.JobConstant;
import com.quanyan.ic.constants.OrderConstant;
import com.quanyan.ic.model.FailedOrderMessage;
import com.quanyan.ic.model.PlaceLightSetting;
import com.quanyan.ic.protocol.LightCmd;
import com.quanyan.ic.protocol.codec.protobuf.ICMessageBuilder;
import com.quanyan.ic.task.TimerTaskJob;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by zhuo.shi on 2017/6/2.
 */
@Component
public class SendICMessageProcessor {

    private static Logger logger = LoggerFactory.getLogger(SendICMessageProcessor.class);
    @Autowired
    private PlaceLightSettingDao placeLightSettingDao;
    @Autowired
    private OrderMsgDao orderMsgDao;
    @Autowired
    private JobClient jobClient;
    @Autowired
    private TimerTaskJob timerTaskJob;

    public void sendICMessage(OrderMessage orderMessage) {
        int placeId = orderMessage.getPlaceId();
        int siteId = orderMessage.getSiteId();
        String orderNo = orderMessage.getOrderNo();
        Channel channel = LightServer.getSessionChannelMap().get(String.valueOf(orderMessage.getPlaceId()));

        PlaceLightSetting setting = placeLightSettingDao.getLightControlSetting(placeId);
        if (setting == null) {
            //此场馆没有灯光设置
            logger.warn("place {} haven't the light IC settings, do not send msg to client", placeId);
            return;
        }

        byte orderStatus = orderMessage.getOrderStatus();

        SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss");

        //场地设置好的自动开关灯时间
        String start_time = setting.getStart_time_indoor();
        String end_time = setting.getEnd_time_indoor();
        if (orderMessage.getSiteType() == OrderConstant.SITE_TYPE_OUTDOOR) {
            start_time = setting.getStart_time_outdoor();
            end_time = setting.getEnd_time_outdoor();
        }

        long before = setting.getOn_light_before_order_start() * 60 * 1000;
        long after = setting.getOff_light_after_order_end() * 60 * 1000;

        // 开关灯时间初始化
        long order_on_time = orderMessage.getStartTime();
        long light_on_time = order_on_time - setting.getOn_light_before_order_start() * 60 * 1000;
        long order_end_time = orderMessage.getEndTime();
        long light_end_time = order_end_time + setting.getOff_light_after_order_end() * 60 * 1000;

        //当前时间
        Long currentTime = System.currentTimeMillis();

//        //预防系统异常情况，订单结束才收到mq消息，不做任何处理
//        if(currentTime >= order_end_time){
//            orderMsgDao.delFailedChannelMsg(orderNo, placeId, siteId, orderMessage.getStartTime(), orderMessage.getEndTime());
//            logger.warn("当前时间 > 场地预订的结束时间，此消息无效，已从失败集合中删除");
//            return;
//        }

        if (orderStatus == OrderConstant.ORDER_STATUS_COMPLETE) { //下单完成
            logger.info("orderNo={}，订单付款完成...", orderNo);
            //开灯时间点
            if (fmt.format(order_on_time).compareTo(start_time) >= 0 && fmt.format(order_on_time).compareTo(end_time) <= 0) {
                logger.info("orderNo={}, pid={}, sid={}，开灯时间在自动控制时间范围内", orderNo, placeId, siteId);
                if (currentTime >= light_on_time) {
                    logger.info("当前时间 >= 开灯时间，立即开灯");
                    if (orderMsgDao.isOrderTimeSegmentIncludeBuffer(placeId, siteId, before, after)) {  //并且此场地当前时间没有订单
                        sendCmd(channel, orderMessage, LightCmd.ON);
                    }
                } else {
                    //提交自动开灯任务
                    logger.info("当前时间 < 开灯时间，提交自动开灯任务");
                    submitLightCmdTask(orderMessage, LightCmd.ON, light_on_time);
                }
            }

            //关灯时间点
            if (fmt.format(order_end_time).compareTo(start_time) >= 0 && fmt.format(order_end_time).compareTo(end_time) <= 0) {
                logger.info("orderNo={}, pid={}, sid={}，关灯时间在自动控制时间范围内", orderNo, placeId, siteId);
                if (currentTime >= light_end_time) {  //当前时间大于订单的开灯时间

                    if (!orderMsgDao.isOrderTimeSegmentIncludeBuffer(placeId, siteId, before, after)) {  //并且此场地当前时间没有订单
                        sendCmd(channel, orderMessage, LightCmd.OFF);
                        logger.info("当前时间 >= 关灯时间，立即关灯");
                    } else {
                        logger.info("site {} have order time segment, don't send OFF cmd to client", siteId);
                    }
                } else {
                    //提交自动关灯任务
                    logger.info("当前时间 < 关灯时间，提交自动关灯任务");
                    submitLightCmdTask(orderMessage, LightCmd.OFF, light_end_time);
                }
            }

        } else if (orderStatus == OrderConstant.ORDER_STATUS_REFUND) { //订单退款
            logger.info("orderNo={}，订单退款完成...", orderNo);
            if (fmt.format(order_end_time).compareTo(start_time) >= 0 && fmt.format(order_end_time).compareTo(end_time) <= 0) {
                //退款成功立即关灯
                logger.info("退款成功，且订单结束时间落在自动开关灯时间范围内，orderEndTime={}，settingStartTime={}，settingEndTime={}", order_end_time, start_time, end_time);
                if (order_on_time <= currentTime && currentTime <= order_end_time) {  //退款，订单当前正在进行，立即关灯
                    channel.writeAndFlush(ICMessageBuilder.buildBizICMessage(orderMessage, LightCmd.OFF));
                    sendCmd(channel, orderMessage, LightCmd.OFF);
                    logger.info("orderNo={}, pid={}, sid={}，退款成功，当前时间此订单正在进行，立即关灯", orderNo, placeId, siteId);
                }
                if ((light_on_time < currentTime && currentTime < order_on_time) || (order_end_time < currentTime && currentTime < light_end_time)) {
                    if (!orderMsgDao.isOrderTimeSegmentIncludeBuffer(placeId, siteId, before, after)) {  //并且此场地当前时间没有订单
                        sendCmd(channel, orderMessage, LightCmd.OFF);
                        logger.info("当前时间 在buffer时间内，并且场地无订单，立即关灯");
                    }
                }
            }
            //订单退款成功，取消下单时提交的开关灯定时任务
            jobClient.cancelJob(buildTaskId(orderMessage, LightCmd.ON), JobConstant.NODE_GROUP_TRACKER);
            jobClient.cancelJob(buildTaskId(orderMessage, LightCmd.OFF), JobConstant.NODE_GROUP_TRACKER);
        } else {
            //订单状态不正确
            logger.warn("orderMessage: {}, order status invalid", JSON.toJSONString(orderMessage));
        }

    }

    private void sendCmd(Channel channel, OrderMessage orderMessage, LightCmd cmd) {
        if (channel != null) {
            channel.writeAndFlush(ICMessageBuilder.buildBizICMessage(orderMessage, cmd));
            logger.info("send {} cmd to client {}, orderMsg={}", cmd.value() == 0 ? "OFF" : "ON", channel.remoteAddress(), JSON.toJSONString(orderMessage));
        }
        orderMsgDao.saveFailedChannelMsg(FailedOrderMessage.build(orderMessage, cmd));
    }

    /**
     * 提交定时任务
     *
     * @param orderMessage 订单
     * @param lightCmd     开关灯命令
     * @param executeTime  执行时间
     */
    private void submitLightCmdTask(OrderMessage orderMessage, LightCmd lightCmd, long executeTime) {

        Map<String, String> params = new HashMap<>();
        params.put("orderMsg", JSON.toJSONString(orderMessage));
        params.put("lcmd", String.valueOf(lightCmd.value()));

        timerTaskJob.submitTask(params, buildTaskId(orderMessage, lightCmd), executeTime, 0L);
    }

    /**
     * 构建taskId
     */
    private String buildTaskId(OrderMessage orderMessage, LightCmd lightCmd) {
        // xx场馆的xx场地  开/关灯 在 a ~ b 时间。 保证唯一的taskId，因为一个订单内可以订一个场地的多个时间段，每个时间段MQ会收到一条消息
        return JobConstant.SEND_IC_MSG_TASK_ID + "_" + orderMessage.getPlaceId() + "_" + orderMessage.getSiteId()
                + "_" + orderMessage.getStartTime() + "_" + orderMessage.getEndTime() + "_" + lightCmd.value();
    }
}
